﻿#pragma once

#include "../box/memory_allocator.hh"
#include <ctime>
#include <list>
#include <string>
#include <unordered_map>

namespace klogger {
class Appender;
}

namespace kratos {
namespace service {

/**
 * 内存快照.
 */
struct MemorySnapshot {
  /**
   * 不同类型对象的数量信息.
   */
  struct CountInfo {
    std::size_t count{0};
    std::string name;
    std::size_t history_count{0};
  };
  using HashNameMap = std::unordered_map<std::size_t, CountInfo>;
  HashNameMap hash_name_map_; ///< {type_info::hash_code(), CountInfo}
  /**
   * 一次内存分配记录.
   */
  struct MemInfo {
    std::string file_name;      ///< 分配内存的文件名
    int line;                   ///< 分配内存的行号
    std::size_t count_info_key; ///< hash_name_map_内存储的key
  };
  using MemInfoMap = std::unordered_map<std::ptrdiff_t, MemInfo>;
  MemInfoMap mem_info_map_; ///< {分配出去的内存块地址，内存分配记录}
};

/**
 * 内存快照信息.
 */
struct SnapshotInfo {
  std::time_t timestamp;   ///< 时间戳
  MemorySnapshot snapshot; ///< 快照
};

/**
 * 内存分配器实现类.
 */
class MemoryAllocatorImpl : public MemoryAllocator {
#if defined(DEBUG) || defined(_DEBUG)
  MemorySnapshot current_snapshot_; ///< 当前内存快照
  using SnapshotList = std::list<SnapshotInfo>;
  SnapshotList snapshot_list_;
#endif // defined(DEBUG) || defined(_DEBUG)

public:
  MemoryAllocatorImpl();
  virtual ~MemoryAllocatorImpl();
  virtual auto alloc(std::size_t size) -> void * override;
  virtual auto dealloc(void *ptr) -> void override;
  virtual auto alloc(std::size_t size, std::size_t hash_id,
                     const std::string &name, const std::string &file_name,
                     int line) -> void * override;
  virtual auto dump(std::ostream &os) -> void override;
  virtual auto snapshot() -> void override;

public:
  /**
   * 将内存分配统计输出到日志
   * @param level 日志等级
   * @param appender 日志添加器
   */
  auto dump(int level, klogger::Appender *appender) -> void;

private:
  /**
   * 打印内存快照.
   *
   * \param name 快照名称
   * \param os std::ostream
   * \param snapshot 快照
   * \param ts 时间戳
   * \return
   */
  auto print_snapshot(const std::string &name, std::ostream &os,
                      const MemorySnapshot &snapshot, std::time_t ts) -> void;
};

} // namespace service
} // namespace kratos
